package wxpay

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
	"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
	"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	"github.com/wechatpay-apiv3/wechatpay-go/utils"
)

type WxPayConfig struct {
	Appid                      string `json:"appid"`
	MchID                      string `json:"mchID"`
	NotifyUrl                  string `json:"notifyUrl"`
	MchAPIv3Key                string `json:"mchAPIv3Key"`
	MchCertificateSerialNumber string `json:"mchCertificateSerialNumber"`
	PrivateKeyPath             string `json:"privateKeyPath"`
}

type UnifyOrderIn struct {
	OpenId     string    `json:"openId" `
	GoodsName  string    `json:"goodsName" `
	OutTradeNo string    `json:"outTradeNo" `
	PayAmount  int64     `json:"payAmount" `
	TimeExpire time.Time `json:"timeExpire" `
}

type UnifyOrderOut struct {
	TimeStamp string `json:"timeStamp" `
	NonceStr  string `json:"nonceStr" `
	Package   string `json:"package" `
	SignType  string `json:"signType" `
	PaySign   string `json:"paySign" `
	PayInfo   string `json:"payInfo" `

	OpenId     string `json:"openId" `
	OutTradeNo string `json:"outTradeNo" `
}

// 微信支付，统一下单
func UnifyOrder(ctx context.Context, in *UnifyOrderIn) (*UnifyOrderOut, error) {

	wxpay, err := g.Cfg().Get(ctx, "wxpay")
	if err != nil {
		return nil, err
	}

	var wxPayConfig *WxPayConfig
	if err := gconv.Scan(wxpay, &wxPayConfig); err != nil {
		return nil, err
	}

	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath(wxPayConfig.PrivateKeyPath)
	if err != nil {
		log.Fatal("load merchant private key error")
		return nil, err
	}

	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(wxPayConfig.MchID, wxPayConfig.MchCertificateSerialNumber, mchPrivateKey, wxPayConfig.MchAPIv3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		return nil, err
	}

	// 防止名称过长，截取前50个字符 SubStrRune返回unicode字符串
	goodsNameSlice := gstr.SubStrRune(in.GoodsName, 0, 40)
	fmt.Println(goodsNameSlice)

	goodsNameSlice2 := gstr.SubStrRune(in.GoodsName, 0, 100)
	fmt.Println(goodsNameSlice2)

	svc := jsapi.JsapiApiService{Client: client}
	resp, _, err := svc.PrepayWithRequestPayment(ctx,
		jsapi.PrepayRequest{
			Appid:       core.String(wxPayConfig.Appid),
			Mchid:       core.String(wxPayConfig.MchID),
			Description: core.String(goodsNameSlice),
			OutTradeNo:  core.String(in.OutTradeNo),
			TimeExpire:  core.Time(in.TimeExpire),
			NotifyUrl:   core.String(wxPayConfig.NotifyUrl),
			Amount: &jsapi.Amount{
				Currency: core.String("CNY"),
				Total:    core.Int64(in.PayAmount),
			},
			Payer: &jsapi.Payer{
				Openid: core.String(in.OpenId),
			},
		},
	)

	if err != nil {
		return nil, err
	}

	return &UnifyOrderOut{
		TimeStamp: *resp.TimeStamp,
		NonceStr:  *resp.NonceStr,
		Package:   *resp.Package,
		SignType:  *resp.SignType,
		PaySign:   *resp.PaySign,

		OpenId:     in.OpenId,
		OutTradeNo: in.OutTradeNo,
		PayInfo:    gjson.MustEncodeString(resp),
	}, nil
}

// 微信回调信息解码
func WxNotifyParse(ctx context.Context) (*payments.Transaction, error) {

	wxpay, err := g.Cfg().Get(ctx, "wxpay")
	if err != nil {
		return nil, err
	}

	var wxPayConfig *WxPayConfig
	if err := gconv.Scan(wxpay, &wxPayConfig); err != nil {
		return nil, err
	}

	// 获取平台证书访问器
	certVisitor := downloader.MgrInstance().GetCertificateVisitor(wxPayConfig.MchID)
	handler := notify.NewNotifyHandler(wxPayConfig.MchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certVisitor))

	transaction := new(payments.Transaction)
	request := ghttp.RequestFromCtx(ctx).Request
	notifyReq, err := handler.ParseNotifyRequest(context.Background(), request, transaction)
	// 如果验签未通过，或者解密失败
	if err != nil {
		fmt.Println("WxNotifyParse失败")
		fmt.Println(err)
		return nil, err
	}
	// 处理通知内容
	fmt.Println("WxNotifyParse成功")
	fmt.Println(notifyReq.Summary)
	fmt.Println(transaction)

	return transaction, nil
}
